package com.hexinfo.dmpro.admin.service;

import com.hexinfo.dmpro.common.base.BaseRequest;
import com.hexinfo.dmpro.common.base.BaseService;
import com.hexinfo.dmpro.common.base.Page;
import com.hexinfo.dmpro.common.base.PageResult;
import com.hexinfo.dmpro.admin.dao.RoleMapper;
import com.hexinfo.dmpro.admin.dao.UserMapper;
import com.hexinfo.dmpro.common.exception.BadRequestException;
import com.hexinfo.dmpro.common.exception.EntityExistException;
import com.hexinfo.dmpro.admin.model.Menu;
import com.hexinfo.dmpro.admin.model.Role;
import com.hexinfo.dmpro.admin.model.RoleMenu;
import com.hexinfo.dmpro.admin.model.User;
import com.hexinfo.dmpro.common.utils.SecurityUtils;
import com.hexinfo.dmpro.common.utils.StringUtils;
import com.hexinfo.dmpro.common.utils.ValidationUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName RoleService
 * @Describe
 * @Author ml.wang
 * @Date 2021-06-17
 */
@Service
@CacheConfig(cacheNames = "role")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class RoleService extends BaseService {

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private UserMapper userMapper;

    public List<Role> queryAll(BaseRequest baseRequest) {
        return roleMapper.queryRoles(baseRequest);
    }

    public PageResult queryRoles(BaseRequest baseRequest, Page page) {
        return selectPage(roleMapper, "queryRoles", page, baseRequest);

    }

    public Role findByName(String name) { return roleMapper.findByName(name);}

    @Cacheable(key = "'findByUserId:' + #p0")
    public List<Role> findByUserId(Long userId) {
        return roleMapper.findByUserId(userId);
    }

    @Cacheable(key = "#p0")
    public Role findById(long id) {
        return roleMapper.findById(id);
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public void save(Role resources) {
        if (roleMapper.findByName(resources.getName()) != null) {
            throw new EntityExistException(Role.class, "role name", resources.getName());
        }
        resources.setCreateTime(new Timestamp(new Date().getTime()));
        roleMapper.save(resources);
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public void modify(Role resources) {
        Role role = roleMapper.findById(resources.getId());
        ValidationUtil.isNull(role.getId(), "Role", "id", resources.getId());

        role = roleMapper.findByName(resources.getName());
        if (role != null && !role.getId().equals(resources.getId())) {
            throw new EntityExistException(Role.class, "role name", resources.getName());
        }

        role = roleMapper.findByPermission(resources.getPermission());
        if (role != null && !role.getId().equals(resources.getId())) {
            throw new EntityExistException(Role.class, "permission", resources.getPermission());
        }

        roleMapper.modify(resources);
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public void updateMenu(Role resource) {
        // 更新菜单数不为空时， 删除原先关关联并重新插入
        // 若为空时， 只删除原先的 角色-菜单 关联
        // 删除角色-菜单关联表中角色对应的menu
        roleMapper.untiedMenuByRoleId(resource.getId());
        if (resource.getMenus() != null && resource.getMenus().size() != 0) {
            // 重新插入角色对应的menu到角色-菜单关系表中
            resource.getMenus().forEach(menus -> {
                RoleMenu roleMenu = new RoleMenu();
                roleMenu.setRoleId(resource.getId());
                roleMenu.setMenuId(menus.getId());
                roleMapper.modifyMenu(roleMenu);
            });
        }
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public void delete(Set<Long> ids) {
        // 若角色有对应的用户存在，不允许删除
        for (Long id : ids) {
            if (userMapper.findByRoleId(id) > 0) {
                throw new BadRequestException("删除的角色中存在用户正在使用，无法删除！");
            }
        }
        // 删除角色-菜单关系表中角色对应的menu
        roleMapper.untiedMenuByRoleIds(ids);
        // 删除角色
        roleMapper.delete(ids);
    }

    public int getLevels(Integer level) {
        User user = userMapper.findByName(SecurityUtils.getUsername());
        List<Integer> levels = this.findByUserId(user.getId()).stream().map(Role::getLevel).collect(Collectors.toList());
        int min = Collections.min(levels);
        if (level != null) {
            if (level < min) {
                throw new BadRequestException("权限不足，你的角色级别：" + min + "，低于操作的角色级别：" + level);
            }
        }
        return min;
    }

    @Cacheable(key = "'loadPermissionByUser:' + #p0.username")
    public Collection<GrantedAuthority> mapToGrantedAuthorities(User user) {
        List<Role> roles = roleMapper.findByUserId(user.getId());
        Set<String> permissions = roles.stream().filter(role -> StringUtils.isNotBlank(role.getPermission())).map(Role::getPermission).collect(Collectors.toSet());
        permissions.addAll(
                roles.stream().flatMap(role -> role.getMenus().stream())
                        .filter(menu -> StringUtils.isNotBlank(menu.getPermission()))
                        .map(Menu::getPermission).collect(Collectors.toSet())
        );
        return permissions.stream().map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
    }

    public boolean isPlatformUser(User user) {
        List<Role> roles = roleMapper.findByUserId(user.getId());
        for (Role role : roles) {
            if (role.getType() != null && "platform".equalsIgnoreCase(role.getType())) {
                return true;
            }
        }
        return false;
    }

}
